const fs = require('fs');
const EventEmitter = require('events');
// 引入链表，保证写入的顺序
const LinkedList = require('../structure/LinkedList');
class Queue {
    constructor() {
        this.LinkedList = new LinkedList();
    }
    enQueue(data) {
        this.LinkedList.add(data);
    }
    deQueue() {
        return this.LinkedList.remove(0);
    }
}
/**
 * 自定义写入流
 */
class WriteStream extends EventEmitter {
    constructor(path, options = {}) {
        super();

        this.path = path;
        this.flags = options.flags || 'w';
        this.start = options.start || 0;
        this.encoding = options.encoding || 'utf8';
        this.autoClose = options.autoClose;
        this.highWaterMark = options.highWaterMark || 16 * 1024;

        // 打开文件
        this.open();
        // 是否需要触发drain事件
        this.needDrain = false;
        // 正在写入的字节数
        this.writtingLen = 0;
        // 写入偏移量
        this.offset = this.start;
        // 缓存还未写入的数据
        this.cache = new Queue();
        // 是否正在写入 
        this.writing = false;
    }
    /**
     * 打开文件
     */
     open() {
        fs.open(this.path, this.flags, (err, fd) => {
            if (err) {
                return this.emit('error', err);
            } else {
                this.fd = fd;
                this.emit('open', this.fd);
            }
        });
    }
    clearBuffer() {
        let node = this.cache.deQueue();
        if (node) {
            // 取出来节点内容
            let param = node.content;
            // 注意这里调用的是_write而不是write.因为我们是从队列里面取出来的
            // 而进入队列的就已经走过了write方法
            this._write(param.chunk,param.encoding, () => {
                param.callback();
                // 继续递归调用清空队列
                this.clearBuffer();
            })
        } else {
            this.writing = false;
            if (this.needDrain) {
                this.emit('drain');
                this.needDrain = false;
            }
        }
    }
    /**
     * 写入文件
     * @param {写入内容} chunk 
     * @param {编码} encoding 
     * @param {回调} callback 
     * @returns 
     */
    write(chunk, encoding, callback) {
        // chunk一般为string或者buffer、转换为buffer
        chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
        this.writtingLen = chunk.length;
        // 是否达到了期望值
        let flag = this.writtingLen < this.highWaterMark;
        // 当达到期望值的时候，需要触发drain事件
        this.needDrain = !flag;
        if (this.writing) {
            // 有人写就排队
            // 排队的话，所有的东西都要保存起来，chunk, encoding, callback
            this.cache.enQueue({
                chunk,
                encoding,
                callback
            });
        } else {
            // 没人就真正的写
            this._write(chunk, encoding, () => {
                callback();
                // 执行完以前的，还要清空队列
                this.clearBuffer();
            });
            this.writing = true;
        }
        return flag;
    }

    _write(chunk, encoding, callback) {
        // 写入这里依然是拿不到fd
        if (typeof this.fd !== 'number') {
            return this.once('open', () => {
                this._write(chunk, encoding, callback);
            });
        }
        //offset 写入偏移量
        // 这里高版本的node会报错。fs.js:169 throw new ERR_INVALID_CALLBACK(cb);
        fs.write(this.fd, chunk, 0, chunk.length, this.offset, (err, written) => {
            if (err) {
                return this.emit('error', err);
            }
            this.offset += written;
            this.writtingLen -= written;
            callback();
        })
    }
}

module.exports = WriteStream;